# -*- coding: utf8 -*-
"""
point_in_polygon
CPython extension for checking if a polygon contains a given point.
It wraps the PNPOLY point inclusion in polygon test by W. Randolph Franklin
(ref.: https://wrfranklin.org/Research/Short_Notes/pnpoly.html).
The algorithm assumes the polygon to be closed, i.e. that the first and
last point are identical.
## Edge cases
points are considered to be outside the polygon if
they are completely outside the polygon (obviously) or
located on an upper (positive y) or right edge (positive x).
A point on the upper left or lower right corner of a square
would therefore also be considered outside the polygon.
"""
from math import log2
from math import fabs
from math import ceil
from typing import Callable
from tool import range_float
from tool import validate_longitude_or_latitude_format
from tool import transform_longitude_or_latitude_into_float
from grid_encoding_level_n import *
import polygon_contains_point as pinp


def process_point_in_polygon(longitude:float,latitude:float,n_vertices:int, longitude_polygon:list[float],latitude_polygon:list[float],grid_encode_function:Callable[[float,float],str]):
    """
    判断点是否在多边形区域内，如果在，需要进行打码，否则则不需要
    :param longitude: float
    :param latitude: float
    :param longitude_polygon: list
    :param latitude_polygon: list
    :param grid_encode_function: function
    :return: None or str
    """
    result = pinp.point_in_polygon(n_vertices, longitude_polygon, latitude_polygon, longitude, latitude)
    if result:
        # 在区域内需要打码
        grid_code = grid_encode_function(longitude, latitude)
        # print(grid_code)
        return grid_code
    else:
        # 不在区域内，可以不用打码
        return None


def beidou_grid_code_polygon(longitudeList:str, latitudeList:str, level_n:int):
    """
    给出多边形边上线的经纬度表示，构造成多边形。要求经纬度以字符串样式传入，并且逗号分割。经纬度相对应 (long1,lat1),(long2,lat2),(long3,lat3)
    如果最后一个坐标点与第一个坐标点不相同，默认多加一个第一个点，代表的是闭环
    :param longitudeList: str , seperator ; ,demo: "long1;long2;long3"
    :param latitudeList: str , seperator ; ,demo: "lat1;lat2;lat3"
    :param level_n: int , [1,10]
    :return:
    """
    # 经纬度按照 ; 号进行分割
    longitude_list = longitudeList.split(';')
    latitude_ist = latitudeList.split(';')
    point_long_count, point_lat_count = len(longitude_list), len(latitude_ist)
    try:
        assert point_long_count == point_lat_count
    except:
        raise Exception(
            f'longitudeList and latitudeList argument length is not equal,please check,{longitudeList}||{latitudeList}')
    else:
        # 验证传入经纬度字符串格式是否正确
        try:
            longitude_format_list = map(validate_longitude_or_latitude_format, longitude_list)
            latitude_format_list = map(validate_longitude_or_latitude_format, latitude_ist)
        except:
            raise Exception(
                f'longitudeList or latitudeList argument format error,please check,{longitudeList}||{latitudeList}')
        else:
            # 把经纬度转换成小数表示
            longitude_float_list = list(map(transform_longitude_or_latitude_into_float, longitude_format_list))
            latitude_float_list = list(map(transform_longitude_or_latitude_into_float, latitude_format_list))
            # 网格编码函数
            grid_func = grid_encoding_function.get(level_n)
            grid_size_long_leveln = eval(f'grid_long_level{level_n}')
            grid_size_lat_leveln = eval(f'grid_lat_level{level_n}')
            # geosot 切分细力度
            longitude_float_max, longitude_float_min = (max(longitude_float_list),min(longitude_float_list))
            latitude_float_max, latitude_float_min = (max(latitude_float_list),min(latitude_float_list))
            diff_longitude = fabs(longitude_float_max - longitude_float_min)
            diff_latitude = fabs(latitude_float_max - latitude_float_min)
            n_long = ceil(round(log2(diff_longitude / grid_size_long_leveln), 10))
            n_lat = ceil(round(log2(diff_latitude / grid_size_lat_leveln), 10))
            n_max = max(n_long, n_lat)
            delta_long = round(diff_longitude / (2 ** n_max), 12)
            delta_lat = round(diff_latitude / (2 ** n_max), 12)
            # 生成序列
            longitude_generate_list = range_float(longitude_float_min, longitude_float_max, delta_long)
            latitude_generate_list = range_float(latitude_float_min, latitude_float_max, delta_lat)
            # 网格化
            grid_point_list = ((lng, lat, point_long_count, longitude_float_list, latitude_float_list, grid_func) for lng in longitude_generate_list for lat in latitude_generate_list)
            grid_encode_result_list = map(lambda x: process_point_in_polygon(*x), grid_point_list)
            grid_code_set = [grid_code for grid_code in grid_encode_result_list if grid_code is not None]
            # grid_point_list = ([(lng,lat,point_long_count,longitude_float_list,latitude_float_list,grid_func) for lat in latitude_generate_list]
            #                    for lng in longitude_generate_list)
            # grid_encode_result_list = (map(lambda x:process_point_in_polygon(*x), grid_point_lng_list) for grid_point_lng_list in grid_point_list)
            # # print('map over')
            # grid_code_set = [grid_code for column in grid_encode_result_list for grid_code in column if grid_code is not None]
            # # print(len(grid_code_set))
            return {'succeed': 'true', 'grid_code': grid_code_set, 'longitudeList': longitudeList,'latitudeList': latitudeList}


if __name__ == '__main__':
    longitudeList = 'E116°18′45.37″;E116°19′45.37″;E116°19′45.37″;E116°18′45.37″;E116°18′45.37″'
    latitudeList = 'N39°59′35.38″;N39°59′35.38″;N40°0′35.38;N40°0′35.38″;N39°59′35.38″'
    level_n = 5
    # print(beidou_grid_code_polygon(longitudeList, latitudeList, level_n))
    # 性能测试
    from timeit import timeit
    iter_number = 5
    duration = timeit(lambda: beidou_grid_code_polygon(longitudeList, latitudeList, level_n), number=iter_number)
    print('generate grid code-{},iter number {},cost time is {} seconds'.format(level_n, iter_number, duration))
    # generate grid code-5,iter number 5,cost time is 0.005990699995891191 seconds